package ms.jasim.console;

import java.io.IOException;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.logging.Logger;

import ms.spm.IAppContext;
import ms.utils.ExceptionUtil;
import ms.utils.SocketUtil;

public class JasimClient {

	protected static Logger log = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);

	// protected Socket socket;
	protected SocketUtil socket;
	// private BufferedReader inStream;
	// private PrintWriter outStream;
	// protected ObjectOutputStream outObjStream;
	// protected ObjectInputStream inObjStream;

	private Thread commandThread;
	private Object datamode_locker = new Object();

	private String serverAddr;
	private int serverPort;
	private String serverUrl;
	protected IAppContext context;
	private boolean commandRunning;

	public JasimClient(IAppContext context) throws IOException {
		this.context = context;
	}

	public void connect(String serverAddress, int serverPort) throws Exception {
		if (Connected())
			return;
		serverAddr = serverAddress;
		this.serverPort = serverPort;

		serverUrl = String.format("%s@%d", serverAddr, serverPort);

		Socket socket = new Socket();
		socket.connect(new InetSocketAddress(serverAddress, serverPort));
		this.socket = new SocketUtil(socket);

		start();
	}

	public boolean sendCommand(String command, Object... args) throws IOException {
		send(command, args);
		return true;
	}

	public void send(String pattern, Object... args) throws IOException {
		socket.writeString(pattern, args);
	}

	public void send(Serializable object) throws IOException {
		socket.writeObject(object);
	}

	public String readString() throws IOException {
		return socket.readString();
	}

	public <T extends Serializable> T readObject() throws IOException {
		return socket.readObject();
	}

	protected void start() {
		commandThread = new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized (commandThread) {
					commandThread.notify();
				}
				for (boolean shutdown = false; socket != null && !shutdown;) {
					try {
						if (!socket.isAvailable())
							shutdown = true;
						else {
							String cmd = readString();
							String args[] = cmd.split(" ", 2);
							args[0] = args[0].toLowerCase();
							processCommand(args[0], args.length > 1 ? args[1] : "");
						}
					} catch (Exception e) {
						if (commandThread != null) {
							ExceptionUtil.handle(e, context);
							shutdown = true;
						}
					}
				}
				shutdown();
			}

		});

		commandThread.start();
		try {
			synchronized (commandThread) {
				commandThread.wait();	
			}
			Thread.sleep(1000);
		} catch (InterruptedException e) {
		}
		
		
	}

	protected void processCommand(String command, String argument) {
		if ("shutdown".equalsIgnoreCase(command)) {
			// System.out.println("JasimServer is shutdown.");
			shutdown();
		} else if ("data".equalsIgnoreCase(command))
			synchronized (datamode_locker) {
				try {
					datamode_locker.wait();
				} catch (InterruptedException e) {
				}
			}
	}

	protected void commandMode() {
		synchronized (datamode_locker) {
			datamode_locker.notify();
		}
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
		}
	}

	public void shutdown() {
		if (socket != null && commandThread != null) {
			// System.out.print("Closing connection to JasimServer...\n");
			try {
				if (commandThread != null)
					commandThread.interrupt();
				commandThread = null;
				socket.close();
			} catch (Exception e) {
			}
			socket = null;
		}
	}

	public boolean Connected() {
		return socket != null && socket.isAvailable();
	}

	public String ServerUrl() {
		return Connected() ? serverUrl : "diconnected";
	}
}